{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Matplotlib\n",
    "import matplotlib\n",
    "import matplotlib.pyplot as plt\n",
    "from matplotlib.cm import get_cmap\n",
    "from matplotlib import animation\n",
    "\n",
    "import seaborn as sns\n",
    "import numpy as np\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "from torch.autograd import Variable\n",
    "sns.set(rc={\"figure.figsize\": (10, 10)})\n",
    "\n",
    "# Break from default darkgrid for better printing on paper\n",
    "sns.set_style(\"whitegrid\")\n",
    "\n",
    "# External Includes\n",
    "from sklearn.datasets.samples_generator import make_moons\n",
    "from sklearn.neighbors.kde import KernelDensity\n",
    "\n",
    "from torch.autograd import Variable\n",
    "from torch.nn import BCELoss\n",
    "from torch.optim import Adam\n",
    "from torch.utils.data import DataLoader, TensorDataset\n",
    "\n",
    "# Internal Includes\n",
    "from rfml.nn.model import Model\n",
    "from rfml.nn.train import StandardTrainingStrategy, PrintingTrainingListener"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "X, Y = make_moons(n_samples=1000, noise=None)\n",
    "\n",
    "X = X.astype(np.float32)\n",
    "Y = Y.astype(np.float32)\n",
    "\n",
    "fig, ax = plt.subplots(1, 1)\n",
    "ax.set_aspect(\"equal\", \"box\")\n",
    "\n",
    "ax.scatter(X[:,0], X[:,1], c=Y, cmap=get_cmap(\"Set1\"))\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "class OvercomplicatedModel(Model):\n",
    "    \n",
    "    def __init__(self):\n",
    "        super(OvercomplicatedModel, self).__init__(input_samples=2, n_classes=1)\n",
    "\n",
    "        self.n1 = nn.Linear(2, 10000)\n",
    "        self.a1 = nn.ReLU()\n",
    "        self.n2 = nn.Linear(10000, 1000)\n",
    "        self.a2 = nn.ReLU()\n",
    "        self.n3 = nn.Linear(1000, 500)\n",
    "        self.a3 = nn.ReLU()\n",
    "        self.n4 = nn.Linear(500, 1)\n",
    "        self.sigmoid = nn.Sigmoid()\n",
    "\n",
    "    def forward(self, x):\n",
    "        x = self.n1(x)\n",
    "        x = self.a1(x)\n",
    "\n",
    "        x = self.n2(x)\n",
    "        x = self.a2(x)\n",
    "\n",
    "        x = self.n3(x)\n",
    "        x = self.a3(x)\n",
    "\n",
    "        x = self.n4(x)\n",
    "        x = self.sigmoid(x)[:,0]\n",
    "\n",
    "        return x\n",
    "\n",
    "model = OvercomplicatedModel()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "class MyTrainingStrategy(StandardTrainingStrategy):\n",
    "\n",
    "    def __call__(\n",
    "        self, model, training, validation\n",
    "    ):\n",
    "        criterion = BCELoss()\n",
    "\n",
    "        if self.gpu:\n",
    "            model.cuda()\n",
    "            criterion.cuda()\n",
    "\n",
    "        optimizer = Adam(model.parameters(), lr=self.lr)\n",
    "\n",
    "        train_data = DataLoader(\n",
    "            training, shuffle=True, batch_size=self.batch_size\n",
    "        )\n",
    "        val_data = DataLoader(\n",
    "            validation, shuffle=True, batch_size=self.batch_size\n",
    "        )\n",
    "\n",
    "        # Fit the data for the maximum number of epochs, bailing out early if\n",
    "        # the early stopping condition is reached.  Set the initial \"best\" very\n",
    "        # high so the first epoch is always an improvement\n",
    "        best_val_loss = 10e10\n",
    "        epochs_since_best = 0\n",
    "        best_epoch = 0\n",
    "        for epoch in range(0, self.max_epochs):\n",
    "            train_loss = self._train_one_epoch(\n",
    "                model=model, data=train_data, loss_fn=criterion, optimizer=optimizer\n",
    "            )\n",
    "            self._dispatch_epoch_completed(mean_loss=train_loss, epoch=epoch)\n",
    "\n",
    "            val_loss = self._validate_once(\n",
    "                model=model, data=val_data, loss_fn=criterion\n",
    "            )\n",
    "            self._dispatch_validation_completed(mean_loss=val_loss, epoch=epoch)\n",
    "\n",
    "            if val_loss < best_val_loss:\n",
    "                best_val_loss = val_loss\n",
    "                epochs_since_best = 0\n",
    "                best_epoch = epoch\n",
    "                model.save()\n",
    "            else:\n",
    "                epochs_since_best += 1\n",
    "\n",
    "            if epochs_since_best >= self.patience:\n",
    "                break\n",
    "\n",
    "        # Reload the \"best\" weights\n",
    "        model.load()\n",
    "        self._dispatch_training_completed(\n",
    "            best_loss=best_val_loss, best_epoch=best_epoch, total_epochs=epoch\n",
    "        )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "trainer = MyTrainingStrategy(batch_size=128)\n",
    "trainer.register_listener(PrintingTrainingListener())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 0 completed!\n",
      "\t\t-Mean Training Loss: 0.692\n",
      "\t\t-Mean Validation Loss: 0.369\n",
      "Epoch 1 completed!\n",
      "\t\t-Mean Training Loss: 0.394\n",
      "\t\t-Mean Validation Loss: 1.957\n",
      "Epoch 2 completed!\n",
      "\t\t-Mean Training Loss: 1.919\n",
      "\t\t-Mean Validation Loss: 0.277\n",
      "Epoch 3 completed!\n",
      "\t\t-Mean Training Loss: 0.308\n",
      "\t\t-Mean Validation Loss: 0.704\n",
      "Epoch 4 completed!\n",
      "\t\t-Mean Training Loss: 0.738\n",
      "\t\t-Mean Validation Loss: 0.675\n",
      "Epoch 5 completed!\n",
      "\t\t-Mean Training Loss: 0.701\n",
      "\t\t-Mean Validation Loss: 0.471\n",
      "Epoch 6 completed!\n",
      "\t\t-Mean Training Loss: 0.495\n",
      "\t\t-Mean Validation Loss: 0.377\n",
      "Epoch 7 completed!\n",
      "\t\t-Mean Training Loss: 0.343\n",
      "\t\t-Mean Validation Loss: 0.221\n",
      "Epoch 8 completed!\n",
      "\t\t-Mean Training Loss: 0.244\n",
      "\t\t-Mean Validation Loss: 0.207\n",
      "Epoch 9 completed!\n",
      "\t\t-Mean Training Loss: 0.202\n",
      "\t\t-Mean Validation Loss: 0.215\n",
      "Epoch 10 completed!\n",
      "\t\t-Mean Training Loss: 0.232\n",
      "\t\t-Mean Validation Loss: 0.235\n",
      "Epoch 11 completed!\n",
      "\t\t-Mean Training Loss: 0.237\n",
      "\t\t-Mean Validation Loss: 0.214\n",
      "Epoch 12 completed!\n",
      "\t\t-Mean Training Loss: 0.187\n",
      "\t\t-Mean Validation Loss: 0.172\n",
      "Epoch 13 completed!\n",
      "\t\t-Mean Training Loss: 0.141\n",
      "\t\t-Mean Validation Loss: 0.158\n",
      "Epoch 14 completed!\n",
      "\t\t-Mean Training Loss: 0.123\n",
      "\t\t-Mean Validation Loss: 0.124\n",
      "Epoch 15 completed!\n",
      "\t\t-Mean Training Loss: 0.123\n",
      "\t\t-Mean Validation Loss: 0.126\n",
      "Epoch 16 completed!\n",
      "\t\t-Mean Training Loss: 0.126\n",
      "\t\t-Mean Validation Loss: 0.133\n",
      "Epoch 17 completed!\n",
      "\t\t-Mean Training Loss: 0.123\n",
      "\t\t-Mean Validation Loss: 0.130\n",
      "Epoch 18 completed!\n",
      "\t\t-Mean Training Loss: 0.112\n",
      "\t\t-Mean Validation Loss: 0.107\n",
      "Epoch 19 completed!\n",
      "\t\t-Mean Training Loss: 0.095\n",
      "\t\t-Mean Validation Loss: 0.083\n",
      "Epoch 20 completed!\n",
      "\t\t-Mean Training Loss: 0.078\n",
      "\t\t-Mean Validation Loss: 0.068\n",
      "Epoch 21 completed!\n",
      "\t\t-Mean Training Loss: 0.063\n",
      "\t\t-Mean Validation Loss: 0.059\n",
      "Epoch 22 completed!\n",
      "\t\t-Mean Training Loss: 0.052\n",
      "\t\t-Mean Validation Loss: 0.058\n",
      "Epoch 23 completed!\n",
      "\t\t-Mean Training Loss: 0.045\n",
      "\t\t-Mean Validation Loss: 0.078\n",
      "Epoch 24 completed!\n",
      "\t\t-Mean Training Loss: 0.042\n",
      "\t\t-Mean Validation Loss: 0.056\n",
      "Epoch 25 completed!\n",
      "\t\t-Mean Training Loss: 0.037\n",
      "\t\t-Mean Validation Loss: 0.047\n",
      "Epoch 26 completed!\n",
      "\t\t-Mean Training Loss: 0.028\n",
      "\t\t-Mean Validation Loss: 0.040\n",
      "Epoch 27 completed!\n",
      "\t\t-Mean Training Loss: 0.021\n",
      "\t\t-Mean Validation Loss: 0.061\n",
      "Epoch 28 completed!\n",
      "\t\t-Mean Training Loss: 0.016\n",
      "\t\t-Mean Validation Loss: 0.022\n",
      "Epoch 29 completed!\n",
      "\t\t-Mean Training Loss: 0.014\n",
      "\t\t-Mean Validation Loss: 0.020\n",
      "Epoch 30 completed!\n",
      "\t\t-Mean Training Loss: 0.012\n",
      "\t\t-Mean Validation Loss: 0.022\n",
      "Epoch 31 completed!\n",
      "\t\t-Mean Training Loss: 0.010\n",
      "\t\t-Mean Validation Loss: 0.014\n",
      "Epoch 32 completed!\n",
      "\t\t-Mean Training Loss: 0.008\n",
      "\t\t-Mean Validation Loss: 0.012\n",
      "Epoch 33 completed!\n",
      "\t\t-Mean Training Loss: 0.006\n",
      "\t\t-Mean Validation Loss: 0.008\n",
      "Epoch 34 completed!\n",
      "\t\t-Mean Training Loss: 0.004\n",
      "\t\t-Mean Validation Loss: 0.008\n",
      "Epoch 35 completed!\n",
      "\t\t-Mean Training Loss: 0.003\n",
      "\t\t-Mean Validation Loss: 0.007\n",
      "Epoch 36 completed!\n",
      "\t\t-Mean Training Loss: 0.002\n",
      "\t\t-Mean Validation Loss: 0.007\n",
      "Epoch 37 completed!\n",
      "\t\t-Mean Training Loss: 0.002\n",
      "\t\t-Mean Validation Loss: 0.007\n",
      "Epoch 38 completed!\n",
      "\t\t-Mean Training Loss: 0.002\n",
      "\t\t-Mean Validation Loss: 0.007\n",
      "Epoch 39 completed!\n",
      "\t\t-Mean Training Loss: 0.001\n",
      "\t\t-Mean Validation Loss: 0.007\n",
      "Epoch 40 completed!\n",
      "\t\t-Mean Training Loss: 0.001\n",
      "\t\t-Mean Validation Loss: 0.006\n",
      "Epoch 41 completed!\n",
      "\t\t-Mean Training Loss: 0.001\n",
      "\t\t-Mean Validation Loss: 0.005\n",
      "Epoch 42 completed!\n",
      "\t\t-Mean Training Loss: 0.001\n",
      "\t\t-Mean Validation Loss: 0.003\n",
      "Epoch 43 completed!\n",
      "\t\t-Mean Training Loss: 0.001\n",
      "\t\t-Mean Validation Loss: 0.002\n",
      "Epoch 44 completed!\n",
      "\t\t-Mean Training Loss: 0.000\n",
      "\t\t-Mean Validation Loss: 0.002\n",
      "Epoch 45 completed!\n",
      "\t\t-Mean Training Loss: 0.000\n",
      "\t\t-Mean Validation Loss: 0.001\n",
      "Epoch 46 completed!\n",
      "\t\t-Mean Training Loss: 0.000\n",
      "\t\t-Mean Validation Loss: 0.001\n",
      "Epoch 47 completed!\n",
      "\t\t-Mean Training Loss: 0.000\n",
      "\t\t-Mean Validation Loss: 0.001\n",
      "Epoch 48 completed!\n",
      "\t\t-Mean Training Loss: 0.000\n",
      "\t\t-Mean Validation Loss: 0.001\n",
      "Epoch 49 completed!\n",
      "\t\t-Mean Training Loss: 0.000\n",
      "\t\t-Mean Validation Loss: 0.000\n",
      "Training has Completed:\n",
      "\n",
      "=======================\n",
      "\tBest Validation Loss: 0.000\n",
      "\tBest Epoch: 49\n",
      "\tTotal Epochs: 49\n",
      "=======================\n"
     ]
    }
   ],
   "source": [
    "split_idx = 100\n",
    "training = TensorDataset(torch.from_numpy(X[:split_idx,:]), torch.from_numpy(Y[:split_idx]))\n",
    "validation = TensorDataset(torch.from_numpy(X[split_idx:,:]), torch.from_numpy(Y[split_idx:]))\n",
    "trainer(model, training, validation)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "def generateGrid(spacing=0.25):\n",
    "    xp = np.arange(-1.5, 2.25, spacing).astype(np.float32)\n",
    "    yp = np.arange(-1, 1.25, spacing).astype(np.float32)\n",
    "    x,y = np.meshgrid(xp, yp)\n",
    "    return x, y\n",
    "\n",
    "def getOutputs(x, y, net):\n",
    "    z = np.zeros(x.shape)\n",
    "    for i in range(x.shape[0]):\n",
    "        for j in range(x.shape[1]):\n",
    "            example = np.stack((x[i,j],y[i,j])).T[np.newaxis,:]\n",
    "            temp = net(torch.from_numpy(example))\n",
    "            temp = temp[0]\n",
    "            z[i, j] = temp.detach().numpy()>0.5\n",
    "    return z\n",
    "\n",
    "def plotOutputs(ax, x, y, z):\n",
    "    cmap = plt.get_cmap('Set1')\n",
    "    CS = ax.pcolormesh(x, y, z, cmap=cmap, alpha=0.25)\n",
    "    return CS"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "model.cpu()\n",
    "\n",
    "x, y = generateGrid(spacing=0.025)\n",
    "z = getOutputs(x, y, model)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 540x540 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, ax = plt.subplots(1,1, figsize=(7.5,7.5))\n",
    "\n",
    "# plotting decision regions\n",
    "CS = plotOutputs(ax, x, y, z)\n",
    "\n",
    "# Plot the true data on top\n",
    "ax.scatter(X[:,0], X[:,1], alpha=0.3, c=Y[:], cmap=get_cmap(\"Set1\"))\n",
    "\n",
    "ax.set_xlim(-1.5, 2.25)\n",
    "ax.set_ylim(-1, 1.25)\n",
    "\n",
    "for side in ['bottom','right']:\n",
    "    ax.spines[side].set_visible(False)\n",
    "\n",
    "ax.get_xaxis().set_visible(False)\n",
    "ax.get_yaxis().set_visible(False)\n",
    "\n",
    "xmin, xmax = ax.get_xlim()\n",
    "ymin, ymax = ax.get_ylim()\n",
    "\n",
    "_ = ax.annotate(\"\", xy=(xmax + 0.2, ymin), xytext=(xmin -0.2, ymin), annotation_clip=False,\n",
    "                arrowprops={\"width\": 5.0, \"facecolor\": \"k\", \"edgecolor\": \"k\"})\n",
    "_ = ax.text(xmax + 0.2, ymin, r\"$X_1$\",\n",
    "            fontsize=16, fontstyle=\"italic\", fontweight=\"bold\",\n",
    "            horizontalalignment=\"left\", verticalalignment=\"center\")\n",
    "\n",
    "_ = ax.annotate(\"\", xy=(xmin, ymax+0.15), xytext=(xmin, ymin - 0.15), annotation_clip=False,\n",
    "                arrowprops={\"width\": 5.0, \"facecolor\": \"k\", \"edgecolor\": \"k\"})\n",
    "_ = ax.text(xmin, ymax+0.15, r\"$X_2$\",\n",
    "            fontsize=16, fontstyle=\"italic\", fontweight=\"bold\",\n",
    "            horizontalalignment=\"center\", verticalalignment=\"bottom\")\n",
    "\n",
    "plt.show()\n",
    "\n",
    "fig.savefig(\"/home/bflowers/toy-adversarial.pdf\", format=\"pdf\", transparent=False)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
